#include<stdio.h>
#include<stdlib.h>
#include<stdint.h>
#include<string.h>
#include<hs/hs.h>

static const char *gPats[] = {
    "(2345A|23456|34567|45678|56789|6789T|"
    "789JT|89JQT|9JKQT|AJKQT)"
    "#(S{5}|H{5}|D{5}|C{5})",
    "(2{4}|3{4}|4{4}|5{4}|6{4}|7{4}|8{4}|"
    "9{4}|T{4}|J{4}|Q{4}|K{4}|A{4}).*#.*",
    "((2{3}|3{3}|4{3}|5{3}|6{3}|7{3}|8{3}|"
    "9{3}|T{3}|J{3}|Q{3}|K{3}|A{3})"
    "(2{2}|3{2}|4{2}|5{2}|6{2}|7{2}|8{2}|"
    "9{2}|T{2}|J{2}|Q{2}|K{2}|A{2}))|"      // 三张加一对
    "((2{2}|3{2}|4{2}|5{2}|6{2}|7{2}|8{2}|"
    "9{2}|T{2}|J{2}|Q{2}|K{2}|A{2})"
    "(2{3}|3{3}|4{3}|5{3}|6{3}|7{3}|8{3}|"
    "9{3}|T{3}|J{3}|Q{3}|K{3}|A{3})).*#.*", // 一对加三张
    ".*#(S{5}|H{5}|D{5}|C{5})",
    "(2345A|23456|34567|45678|56789|6789T|"
    "789JT|89JQT|9JKQT|AJKQT)#.*",
    "(2{3}|3{3}|4{3}|5{3}|6{3}|7{3}|8{3}|"
    "9{3}|T{3}|J{3}|Q{3}|K{3}|A{3}).*#.*",
    "(2{2}|3{2}|4{2}|5{2}|6{2}|7{2}|8{2}|"
    "9{2}|T{2}|J{2}|Q{2}|K{2}|A{2}).*"
    "(2{2}|3{2}|4{2}|5{2}|6{2}|7{2}|8{2}|"
    "9{2}|T{2}|J{2}|Q{2}|K{2}|A{2}).*#.*",
    "(2{2}|3{2}|4{2}|5{2}|6{2}|7{2}|8{2}|"
    "9{2}|T{2}|J{2}|Q{2}|K{2}|A{2}).*#.*"
};

char *gPokerType[] = {"Straight flush", "Four of a kind", "Full house",
                      "Flush", "Straight", "Three of a kind", "Two pair",
                      "One pair", "High card"};

// 编译数据结构
typedef struct hs_db_scratch{
    hs_database_t *db;
    hs_scratch_t *scratch;
}hs_db_scratch_t;

// 排序比较函数
int cmp(const void *p, const void *q);
// 将牌中的字母变为大写
void upper_hand(char *in_hand);
// 将牌分为用#分隔的等级和花色两部分，并对两部分分别排序
void sort_hand(char *in_hand);
// 牌型判断
int classify_poker(const char *in_hand, hs_db_scratch_t *p_ds, int n);

static int hs_prepare_scan(const char **gPats, hs_db_scratch_t *p_ds, int n);
static int on_match(unsigned int id, unsigned long long from,
        unsigned long long to, unsigned int flags, void *ctx);

// 测试/驱动
int main()
{
    int i, ret;
    char in_hand[][12] = {"2s5s4s3s6s", "8cas7cad3h", "6s2d9c4hts",
                          "asqsjsksts", "asqcjdkhts", "1s5sjsksts"
                         };

    hs_db_scratch_t ds[8] = {0};
    // 编译正则表达式
    hs_prepare_scan(gPats, ds, 8);

    for(i = 0; i < sizeof(in_hand) / sizeof(in_hand[0]); i++)
    {
        upper_hand(in_hand[i]);
        sort_hand(in_hand[i]);
        printf("%s: ", in_hand[i]);
        ret = classify_poker(in_hand[i], ds, 8);
        puts(gPokerType[ret]);
    }

    // 释放空间
    for(i = 0; i < 8; i++)
    {
        hs_free_database(ds[i].db);
        hs_free_scratch(ds[i].scratch);
    }

    return 0;
}

static int hs_prepare_scan(const char **gPats, hs_db_scratch_t *p_ds, int n)
{
    int i, j;
    hs_error_t error;
    hs_compile_error_t *compile_err;

    // 编译
    for(i = 0; i < n; i++)
    {
        error = hs_compile(gPats[i], HS_FLAG_SINGLEMATCH | HS_FLAG_DOTALL,
                HS_MODE_BLOCK, NULL, &p_ds[i].db, &compile_err);
        if(error != HS_SUCCESS)
        {
            fprintf(stderr, "ERROR: Unable to compile pattern \"%s\": %s\n",
                    gPats[i], compile_err->message);
            hs_free_compile_error(compile_err);
            for(j = 0; j < i; j++)
            {
                hs_free_database(p_ds[j].db);
                hs_free_scratch(p_ds[j].scratch);
            }
            exit(EXIT_FAILURE);
        }
        error = hs_alloc_scratch(p_ds[i].db, &p_ds[i].scratch);
        if(error != HS_SUCCESS)
        {
            fprintf(stderr, "ERROR: Unable to allocate scratch space."
                            " Exiting.\n");
            for(j = 0; j < i; j++)
            {
                hs_free_database(p_ds[j].db);
                hs_free_scratch(p_ds[j].scratch);
            }
            hs_free_database(p_ds[j].db);
            exit(EXIT_FAILURE);
        }
    }

    return 0;
}

static int on_match(unsigned int id, unsigned long long from,
        unsigned long long to, unsigned int flags, void *ctx)
{
    int *ret = (int*)ctx;
    *ret = 1;

    return 1; // 返回0继续匹配，返回1停止匹配
}

// 牌型判断
// 要求等级与花色用#分隔且要求等级与花色分别进行了排序
int classify_poker(const char *in_hand, hs_db_scratch_t *p_ds, int n)
{
    int i;
    int flag = 0;

    for(i = 0; i < n; i++)
    {
        flag = 0;
        hs_scan(p_ds[i].db, in_hand, strlen(in_hand), 0,
                p_ds[i].scratch, on_match, &flag);
        if(flag)
        {
            return i;
        }
    }

    return i;
}

// 将牌用#分为等级和花色两部分，并对两部分进行排序
void sort_hand(char *s)
{
    char face[6] = {0};
    char suit[6] = {0};
    int i = 0;
    char *p = s;

    while(*p)
    {
        if(i % 2 == 0)
        {
            face[i / 2] = *p;
        }
        else
        {
            suit[i / 2] = *p;
        }
        i++;
        p++;
    }

    qsort(face, strlen(face), 1, cmp);
    qsort(suit, strlen(suit), 1, cmp);
    strcpy(s, face);
    strcat(s, "#");
    strcat(s, suit);
}

// 将字符串中的小写字母变换为大写，其它字符不变
void upper_hand(char *s)
{
    while(*s)
    {
        if(*s >= 'a' && *s <= 'z')
        {
            *s ^= 0x20; // 转换为大写
        }
        s++;
    }
}

// 排序比较函数
int cmp(const void *p, const void *q)
{
    char ch_p = *((char*)p);
    char ch_q = *((char*)q);

    return (ch_p > ch_q) - (ch_p < ch_q);
}
